#pragma once 
#include "ELog_def.h" 
#include "single.h"

class CELog
{
public:

	ElogErrCode elog_init(void);
	void elog_start(void);
	void elog_set_output_enabled(bool enabled);
	bool elog_get_output_enabled(void);
	void elog_set_fmt(uint8_t level, size_t set);
	void elog_set_filter(uint8_t level, const char *tag, const char *keyword);
	void elog_set_filter_lvl(uint8_t level);
	void elog_set_filter_tag(const char *tag);
	void elog_set_filter_kw(const char *keyword);
	void elog_raw(const char *format, ...);
	void elog_output(uint8_t level, const char *tag, const char *file, const char *func,
		const long line, const char *format, ...);
	void elog_output_lock_enabled(bool enabled);
	void elog_assert_set_hook(void(*hook)(const char* expr, const char* func, size_t line));
	size_t elog_strcpy(size_t cur_len, char *dst, const char *src);

	bool output_lock_enabled;
	bool output_is_locked_before_enable;
	bool output_is_locked_before_disable;
	void output_lock(void);
	void output_unlock(void);
	bool get_fmt_enabled(uint8_t level, size_t set);
};

extern void(*elog_assert_hook)(const char* expr, const char* func, size_t line);

#define ELOG singleton_t<CELog>::instance()

#ifndef ELOG_OUTPUT_ENABLE 

#define elog_a(tag, ...) 
#define elog_e(tag, ...) 
#define elog_w(tag, ...) 
#define elog_i(tag, ...) 
#define elog_d(tag, ...) 
#define elog_v(tag, ...) 

#else /* ELOG_OUTPUT_ENABLE */ 

#if ELOG_OUTPUT_LVL >= ELOG_LVL_ASSERT 
#define elog_a(tag, ...) \
ELOG->elog_output(ELOG_LVL_ASSERT, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#else 
#define elog_a(tag, ...) 
#endif 

#if ELOG_OUTPUT_LVL >= ELOG_LVL_ERROR 
#define elog_e(tag, ...) \
ELOG->elog_output(ELOG_LVL_ERROR, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#else 
#define elog_e(tag, ...) 
#endif 

#if ELOG_OUTPUT_LVL >= ELOG_LVL_WARN 
#define elog_w(tag, ...) \
ELOG->elog_output(ELOG_LVL_WARN, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#else 
#define elog_w(tag, ...) 
#endif 

#if ELOG_OUTPUT_LVL >= ELOG_LVL_INFO 
#define elog_i(tag, ...) \
ELOG->elog_output(ELOG_LVL_INFO, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#else 
#define elog_i(tag, ...) 
#endif 

#if ELOG_OUTPUT_LVL >= ELOG_LVL_DEBUG 
#define elog_d(tag, ...) \
ELOG->elog_output(ELOG_LVL_DEBUG, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#else 
#define elog_d(tag, ...) 
#endif 

#if ELOG_OUTPUT_LVL == ELOG_LVL_VERBOSE 
#define elog_v(tag, ...) \
ELOG->elog_output(ELOG_LVL_VERBOSE, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#else 
#define elog_v(tag, ...) 
#endif 

#endif /* ELOG_OUTPUT_ENABLE */ 

#ifdef ELOG_ASSERT_ENABLE 
#define ELOG_ASSERT(EXPR) \
if (!(EXPR)) \
{ \
if (elog_assert_hook == NULL) {\
	\
elog_a("elog", "(%s) has assert failed at %s:%ld.", #EXPR, __FUNCTION__, __LINE__); \
	while (1); \
}\
else {\
	\
elog_assert_hook(#EXPR, __FUNCTION__, __LINE__); \
} \
}
#else 
#define ELOG_ASSERT(EXPR) ((void)0); 
#endif 


/*
Impl
*/

extern ElogErrCode elog_port_init(void);
extern void elog_port_output(const char *log, size_t size);
extern void elog_port_output_lock(void);
extern void elog_port_output_unlock(void);
extern const char *elog_port_get_time(void);
extern const char *elog_port_get_p_info(void);
extern const char *elog_port_get_t_info(void);